#include "toolutils.h"
#include "dragabletab.h"
#include "imageeditor.h"
#include "svideowidget.h"

#include <QFile>
#include <QFileDialog>
#include <QPlainTextEdit>
#include <QMimeDatabase>

#ifdef _MSC_VER
#define bswap16 _byteswap_ushort
#define bswap32 _byteswap_ulong
#elif __GNUC__
#define bswap16 __builtin_bswap16
#define bswap32 __builtin_bswap32
#pragma GCC diagnostic ignored "-Wmultichar"
#else
#error "Unsupported compiler"
#endif

#include <QApplication>
#include <QMainWindow>
#include <QTextCodec>
#include <QMap>
#include <cmath>

#include <QPropertyAnimation>
#include <QEventLoop>

// TODO:
// 1、学习一下windowsApi，重点是转码相关的那几个函数
// 2、再研究一下ID3文档

namespace {
    constexpr int ID3VER_23 = 3;
    constexpr int ID3VER_24 = 4;
    constexpr unsigned int PICTURE = 'APIC';
    constexpr unsigned int COMMENT = 'COMM';
    constexpr unsigned int ALBUM = 'TALB';
    constexpr unsigned int COMPOSER =  'TCOM';
    constexpr unsigned int GENRE = 'TCON';
    constexpr unsigned int LYRIC_LIST = 'TEXT';
    constexpr unsigned int TITLE = 'TIT2';
    constexpr unsigned int ORIGINAL_ARTIST = 'TOPE';
    constexpr unsigned int ARTIST = 'TPE1';
    constexpr unsigned int TRACK = 'TRCK';
    constexpr unsigned int YEAR = 'TYER';
    constexpr char ENCODING_ISO_8859_1  = 0x00;
    constexpr char ENCODING_UTF16_BOM = 0x01;
    constexpr char ENCODING_UTF16BE = 0x02;
    constexpr char ENCODING_UTF8 = 0x03;
    const QString MIME_JPEG = "image/jpeg";
    const QString MIME_PNG = "image/png";
    constexpr double EPCILION = 0.01;
}

ToolUtils::ToolUtils() {}

bool ToolUtils::EasyDialogReturnBool(const QString& title, const QString& text)
{
    QMessageBox msgBox;
    msgBox.setWindowTitle(title);
    msgBox.setText(text);
    msgBox.setWindowIcon(QIcon::fromTheme(QIcon::ThemeIcon::Scanner));
    msgBox.setStandardButtons(QMessageBox::Yes | QMessageBox::No); // 设置确定和取消按钮
    msgBox.setIcon(QMessageBox::Question); // 设置图标为问题

    // 弹出对话框并获取用户选择
    return msgBox.exec() == QMessageBox::Yes ? true : false;
}

std::pair<DragableTab*, QWidget*> ToolUtils::OpenPage(QWidget* parentWidget)
{
    QString fileName = QFileDialog::getOpenFileName(parentWidget, tr("打开文件"),
                                                    QCoreApplication::applicationFilePath(),
                                                    tr("任何文件 (*.*)"));
    // tr("文本文件 (*.txt *.cpp *.h);;图片文件 (*.png *.jpg *.bmp));;视频文件 (*.))"
    QFileInfo fileInfo(fileName);
    if (!fileInfo.exists() || !fileInfo.isFile()) {
        return std::pair(nullptr, nullptr);
    }

    qDebug("file suffix=%s", fileInfo.completeSuffix().toStdString().c_str());
    qDebug("file type=%s", ToolUtils::CheckFileType(fileName).toStdString().c_str());

    QFile file(fileName);
    if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
        QMessageBox::warning(parentWidget, "警告", "由于未知原因打开失败!");
        return std::pair(nullptr, nullptr);
    }

    QByteArray byteArray = file.readAll();
    QWidget* widget = nullptr;
    DragableTab::TabType tabType = DragableTab::TabType::DEFAULT;
    auto type = ToolUtils::CheckFileType(fileName);
    if (type == "text") {
        auto textEdit = new QPlainTextEdit(parentWidget);
        textEdit->setPlainText(byteArray);
        widget = textEdit;
        tabType = DragableTab::TabType::TEXT;
    }

    if (type == "image") {
        auto view = new ImageEditor(fileName, parentWidget);
        view->setAlignment(Qt::AlignCenter);
        view->setAcceptDrops(false);
        widget = view;
        tabType = DragableTab::TabType::IMAGE;
    }

    if (type == "audio" || type == "video") {
        auto sVideoWidget = new SVideoWidget(fileName, parentWidget);
        sVideoWidget->setAcceptDrops(false);
        widget = sVideoWidget;
        tabType = DragableTab::TabType::MUSIC;
    }
    file.close();
    auto title = fileName.sliced(fileName.lastIndexOf("/") + 1);
    return widget != nullptr
        ? std::pair<DragableTab*, QWidget*>(new DragableTab(title, tabType, parentWidget), widget)
        : std::pair(nullptr, nullptr);
}

bool ToolUtils::SavePage(std::pair<DragableTab*, QWidget*> page, QWidget* parentWidget)
{
    if (!page.first || !page.second) {
        return false;
    }

    if (!QFile::exists(page.first->FilePath())) {
        return SavePageAs(page, parentWidget);
    }

    QFile file(page.first->FilePath());
    if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
        QMessageBox::warning(parentWidget, "警告", "由于未知原因保存失败!");
        return false;
    };

    switch (page.first->ContentType()) {
    case DragableTab::TabType::TEXT:
    {
        auto textEdit = qobject_cast<QPlainTextEdit*>(page.second);
        file.write(textEdit->toPlainText().toStdString().c_str());
        break;
    }
    default:
        break;
    }
    file.close();
    return true;
}

bool ToolUtils::SavePageAs(std::pair<DragableTab*, QWidget*> page, QWidget* parentWidget)
{
    QString fileName = QFileDialog::getSaveFileName(
        parentWidget, "另存为","./", tr("txt (*.txt);;*"));
    if (fileName.isEmpty()) {
        return false;
    }
    QFile file(fileName);
    if (!file.open(QIODevice::WriteOnly| QIODevice::Text)) {
        QMessageBox::warning(parentWidget, "警告", "由于未知原因另存为失败!");
        return false;
    };

    switch (page.first->ContentType()) {
    case DragableTab::TabType::TEXT:
    {
        auto textEdit = qobject_cast<QPlainTextEdit*>(page.second);
        file.write(textEdit->toPlainText().toStdString().c_str());
        break;
    }
    default:
        break;
    }

    file.close();
    return true;
}

// TODO:
// 设置一个备忘录，用来存储已经读取过的file文件，读取过的就不在重新读取，已增加效率
// 或者可以定义一下是否重新读取的规则，比如说读取的次数。

QString ToolUtils::ReadStyleSheet(QString qrcPath)
{
    // 读取样式表文件
    QFile styleFile(qrcPath); // 假设样式表文件位于资源中
    if (!styleFile.open(QFile::ReadOnly)) {
        return "";
    }
    QTextStream stream(&styleFile);
    return stream.readAll();
}

QString ToolUtils::CheckFileType(QString filePath)
{
    QMimeDatabase mimeDatabase;
    QMimeType mimeType = mimeDatabase.mimeTypeForFile(filePath);
    return mimeType.name().split("/").first();
}

inline bool ToolUtils::ValidateID3Header(const ID3Header& header)
{
    return (
        // magic == "ID3" -> 0x "49 44 33"
        header.magic[0] == 0x49 &&
        header.magic[1] == 0x44 &&
        header.magic[2] == 0x33 &&
        // 版本和副版本
        (header.ver == 3 || header.ver == 4) &&
        header.rev == 0 &&
        // ID3标准是小于0x80
        header.size[0] < 0x80 &&
        header.size[1] < 0x80 &&
        header.size[2] < 0x80 &&
        header.size[3] < 0x80
    );
}

inline ToolUtils::ID3Flags ToolUtils::ExtractID3HeaderFlags(const ID3Header& header)
{
    ID3Flags flags {};
    flags.unsynchronisation = !!(header.flags & 0x80);  // !! 这里的作用是变成bool类型然后再取反回来
    flags.extendedHeader = !!(header.flags & 0x40);
    flags.experimentalIndicator = !!(header.flags & 0x20);
    flags.footerPresent = !!(header.flags & 0x10);
    return flags;
}

inline unsigned int ToolUtils::ConvertSynchsafeIntgerToInt(const char synchsafeIntger[4])
{
    return synchsafeIntger[0] << 21 | synchsafeIntger[1] << 14 | synchsafeIntger[2] << 7 | synchsafeIntger[3];
}

// 使用memcpy将char数组转换成int
inline unsigned int ToolUtils::ConvertCharsToInt(const char chars[4])
{
    unsigned int uInteger;
    memcpy(&uInteger, chars, 4);
    return bswap32(uInteger);
    // return chars[0] << 24 | chars[1] << 16 | chars[2] << 8 | chars[3];
}

inline ToolUtils::ID3FrameFlags ToolUtils::ExtractID3FrameFlags(const ID3Frame& frame)
{
    ID3FrameFlags flags {};

    flags.isTagAlterDiscard = !!(frame.flags[0] & 0x40);
    flags.isFileAlterDiscard = !!(frame.flags[0] & 0x20);
    flags.isReadOnly = !!(frame.flags[0] & 0x10);
    flags.isGroupInformation = !!(frame.flags[1] & 0x40);
    flags.isCompressed = !!(frame.flags[1] & 0x08);
    flags.isEncrypted = !!(frame.flags[1] & 0x04);
    flags.isUnsyrchronised = !!(frame.flags[1] & 0x02);
    flags.hasDataLengthIndicator = !!(frame.flags[1] & 0x01);

    return flags;
}

// 利用Qt库中的QTextCodec进行字符编码转换
QByteArray ToolUtils::ParseTextFromFrameData(const char encoding, const QByteArray& frameData)
{
    QString text;
    QTextCodec *codec = nullptr;

    if (frameData.length() <= 1) {
        return "";
    }

    switch (encoding) {
        case ENCODING_ISO_8859_1: {
            codec = QTextCodec::codecForName("ISO-8859-1");
            text = codec->toUnicode(frameData);
            break;
        }
        case ENCODING_UTF16_BOM: {
            codec = QTextCodec::codecForName("UTF-16LE");
            text = codec->toUnicode(frameData);
            break;
        }
        case ENCODING_UTF16BE: {
            codec = QTextCodec::codecForName("UTF-16BE");
            text = codec->toUnicode(frameData);
            break;
        }
        case ENCODING_UTF8: {
            codec = QTextCodec::codecForName("UTF8");
            text = codec->toUnicode(frameData);
            break;
        }
    }
    // qDebug() << text;
    return QByteArray::fromStdString(text.toStdString());
}

int ToolUtils::GetTextLength(char encoding, const QByteArray& text)
{
    switch (encoding) {
        case ENCODING_ISO_8859_1:
        case ENCODING_UTF8:
            return strlen(text.constData()) + 1;
        case ENCODING_UTF16_BOM:
        case ENCODING_UTF16BE: {
            int size = 0;
            while (text[size] != '\0' || text[size + 1] != '\0') {
                size += 2;
            }
            size += 2;
            return size;
        }
        default:
            return 0;
    }
}

QByteArray ToolUtils::ExtractPictureFromData(const char encoding, const QByteArray& frameData)
{
    // mimeType类型
    // qDebug("encoding=0x%x", encoding);
    auto mimeTypeLength = GetTextLength(encoding, frameData);
    // -1是为了去掉 \0 也就是0x00
    auto mimeType = ParseTextFromFrameData(
        encoding, frameData.sliced(0, mimeTypeLength - 1));

    // 图片类型
    const auto picType = frameData.at(mimeTypeLength);
    // qDebug("picType=%x", picType);

    // 图片描述
    auto descriptionLength = GetTextLength(
        encoding, frameData.sliced(mimeTypeLength + 1, frameData.length() - mimeTypeLength - 1));
    // qDebug("descriptionLength=%d", descriptionLength);
    auto description = ParseTextFromFrameData(
        encoding, frameData.sliced(mimeTypeLength + 1, descriptionLength - 1));

    // 图片QByteArray
    // const auto image = QImage::fromData(
    //     frameData.last(frameData.length() - mimeTypeLength - descriptionLength - 1));

    return frameData.last(frameData.length() - mimeTypeLength - descriptionLength - 1);
}


QMap<QString, QByteArray> ToolUtils::ExtractInfoFromMP3(const QString& mp3Path)
{
    // 打开文件
    QFile mp3File(mp3Path);
    ID3Header header {};
    if (!mp3File.open(QFile::ReadOnly)) {
        return {};
    }
    // 读取ID3文件头信息
    if (mp3File.read(reinterpret_cast<char*> (&header), sizeof(ID3Header)) < sizeof(ID3Header)
            || !ValidateID3Header(header)) {
        qDebug("ID3Header Error");
        return {};
    }

    // 对相关信息进行解码 headerFlags tagSize
    ID3Flags flags = ExtractID3HeaderFlags(header);
    unsigned int tagSize = ConvertSynchsafeIntgerToInt(header.size);

    // 2、如果有extendedHeader，则读取并跳过
    if (flags.extendedHeader) {
        if (header.ver == ID3VER_23) {
            ID3_23ExtendedHeader extendedHeader {};
            if (mp3File.read(reinterpret_cast<char*> (&extendedHeader), sizeof(ID3_23ExtendedHeader)) <= 0) {
                qDebug("ID3_23ExtendedHeader read error");
                return {};
            }
            // 计算是否有crcData，有的话会是固定的4个字节
            const auto crcDataSize = ConvertCharsToInt(extendedHeader.size) + 4 - sizeof(extendedHeader);
            // 往后seek crcDataSize
            mp3File.seek(crcDataSize + mp3File.pos());
            // tagSize = tagSize - ConvertCharsToInt(extendedHeader.padding) - ConvertCharsToInt(extendedHeader.size) - crcDataSize;
            tagSize -= ConvertCharsToInt(extendedHeader.padding);
        } else if (header.ver == ID3VER_24) {
            ID3_24ExtendedHeader extendedHeader {};
            if (mp3File.read(reinterpret_cast<char*> (&extendedHeader), sizeof(ID3_24ExtendedHeader)) < 0) {
                qDebug("ID3_24ExtendedHeader read error");
                return {};
            }
            // extendedHeaderSize 包括整个extendedHeader的内容和header，所以要减去header
            const auto extendedHeaderSize = ConvertSynchsafeIntgerToInt(extendedHeader.size);
            if (!mp3File.seek(extendedHeaderSize - sizeof(extendedHeader) + mp3File.pos())) {
                qDebug("ID3_24ExtendedHeader seek error");
                return {};
            }
            tagSize -= extendedHeaderSize;
        }
    }
    // 3、读取frames
    unsigned int offset = 0;
    ID3Frame frame {};
    unsigned int frameDataSize = 0;
    ID3FrameFlags frameFlag {};
    unsigned int frameId = 0;
    QMap<QString, QByteArray> output {};
    // qDebug("ToolUtils::ExtractInfoFromMP3, 222222222222222");
    while (offset < tagSize) {
        // 读取帧头frameHeader
        if (mp3File.read(reinterpret_cast<char*> (&frame), sizeof(frame)) < sizeof(frame))
        {
            qDebug("ID3Frame read error");
            return output;
        }

        if (header.ver == ID3VER_24) {
            frameDataSize = ConvertSynchsafeIntgerToInt(frame.size);
        } else {
            frameDataSize = ConvertCharsToInt(frame.size);
        }
        qDebug("ToolUtils::ExtractInfoFromMP3, frameDataSize=%u", frameDataSize);

        // 提取相关标志位，如果加密或者压缩了的，则continue
        frameFlag = ExtractID3FrameFlags(frame);
        if (frameFlag.isCompressed || frameFlag.isEncrypted) {
            qDebug("frameFlag.isCompressed || frameFlag.isEncrypted, continue.");
            if (!mp3File.seek(mp3File.pos() + frameDataSize)) {
                qDebug("ID3Frame seek error");
                return output;
            }
            offset += sizeof(frame) + frameDataSize;
            continue;
        }

        // 根据size读取frameData
        // const auto frameData = new char[frameDataSize]{};
        if (frameDataSize == 0) {
            return output;
        }

        QByteArray frameData = mp3File.read(frameDataSize);
        if (frameData.length() < frameDataSize) {
            qDebug("ID3Frame data read error");
            return output;
        }

        frameId = ConvertCharsToInt(frame.frameId);
        const char encoding = frameData.at(0);
        frameData.removeAt(0);
        switch (frameId) {
            case PICTURE: {
                qDebug("ToolUtils::ExtractInfoFromMP3, PICTURE");
                output["PICTURE"] =ExtractPictureFromData(encoding, frameData);
                break;
            }
            case COMMENT: {
                qDebug("ToolUtils::ExtractInfoFromMP3, COMMENT");
                output["COMMENT"] = ParseTextFromFrameData(encoding, frameData);
                break;
            }
            case ALBUM: {
                qDebug("ToolUtils::ExtractInfoFromMP3, ALBUM");
                output["ALBUM"] = ParseTextFromFrameData(encoding, frameData);
                break;
            }
            case COMPOSER: {
                qDebug("ToolUtils::ExtractInfoFromMP3, COMPOSER");
                output["COMPOSER"] = ParseTextFromFrameData(encoding, frameData);
                break;
            }
            case GENRE: {
                qDebug("ToolUtils::ExtractInfoFromMP3, GENRE");
                output["GENRE"] = ParseTextFromFrameData(encoding, frameData);
                break;
            }
            case LYRIC_LIST: {
                qDebug("ToolUtils::ExtractInfoFromMP3, LYRIC_LIST");
                output["LYRIC_LIST"] = ParseTextFromFrameData(encoding, frameData);
                break;
            }
            case TITLE: {
                qDebug("ToolUtils::ExtractInfoFromMP3, TITLE");
                output["TITLE"] = ParseTextFromFrameData(encoding, frameData);
                break;
            }
            case ORIGINAL_ARTIST: {
                qDebug("ToolUtils::ExtractInfoFromMP3, ORIGINAL_ARTIST");
                output["ORIGINAL_ARTIST"] = ParseTextFromFrameData(encoding, frameData);
                break;
            }
            case ARTIST: {
                qDebug("ToolUtils::ExtractInfoFromMP3, ARTIST");
                output["ARTIST"] = ParseTextFromFrameData(encoding, frameData);
                break;
            }
            case TRACK: {
                qDebug("ToolUtils::ExtractInfoFromMP3, TRACK");
                output["TRACK"] = ParseTextFromFrameData(encoding, frameData);
                break;
            }
            case YEAR: {
                qDebug("ToolUtils::ExtractInfoFromMP3, YEAR");
                output["YEAR"] = ParseTextFromFrameData(encoding, frameData);
                break;
            }
            default: {
                qDebug("ToolUtils::ExtractInfoFromMP3, %s", frame.frameId);
                break;
            }
        }
        offset += sizeof(frame) + frameDataSize;
    }
    // qDebug() << "ToolUtils::ExtractInfoFromMP3," << output;
    mp3File.close();
    return output;
}

QImage ToolUtils::GaussianBlur(const QImage &src, int radius, double sigma) {
    // 创建临时图像
    auto dest = QImage(src.size(), QImage::Format_ARGB32);
    dest.fill(Qt::transparent);

    if (sigma <= EPCILION || radius <= 1) {
        return dest;
    }

    // 生成高斯模糊滤波器
    auto GaussianWeightFunc = [sigma] (const int x, const int y) {
        return exp(-((x*x) + (y*y)) / (2*sigma*sigma)) /
            (2 * M_PI * sigma * sigma);
    };

    std::vector<std::vector<double>> GaussianMatrix(radius*2, std::vector<double>(radius*2));
    for (int x = -radius; x < radius; x++) {
        for (int y = -radius; y < radius; y++) {
            GaussianMatrix[x + radius][y + radius] = GaussianWeightFunc(x, y);
        }
    }

    auto GaussianBlurFunc = [radius, &src, &GaussianMatrix] (int xPos, int yPos) {
        double sumR = 0;
        double sumG = 0;
        double sumB = 0;
        double sumA = 0;
        // 暂时不需要去弥补权重矩阵不够的情况，这样刚好会使屏幕变暗，一举两得
        constexpr double weights = 1.;
        for (int x = -radius; x < radius; x++) {
            for (int y = -radius; y < radius; y++) {
                if (xPos + x < 0 || xPos + x >= src.width()
                    || yPos + y < 0 || yPos + y >= src.height()) continue;
                QColor color = src.pixel(xPos + x, yPos + y);
                sumR += color.red() * GaussianMatrix[x + radius][y + radius];
                sumG += color.green() * GaussianMatrix[x + radius][y + radius];
                sumB += color.blue() * GaussianMatrix[x + radius][y + radius];
                sumA += color.alpha() * GaussianMatrix[x + radius][y + radius];
                // weights += GaussianMatrix[x + radius][y + radius];
            }
        }
        return QColor(
                static_cast<int>(sumR / weights),
                static_cast<int>(sumG / weights),
                static_cast<int>(sumB / weights),
                static_cast<int>(sumA / weights));
    };

    // 水平方向高斯模糊
    for (int y = 0; y < src.height(); ++y) {
        for (int x = 0; x < src.width(); ++x) {
            dest.setPixel(x, y, GaussianBlurFunc(x,y).rgba());
        }
    }
    return dest;
}


bool ToolUtils::IsCursorInWindow()
{
    foreach (QWidget *widget, QApplication::topLevelWidgets()) {
        // 检查当前窗口是否为主窗口（假设主窗口是QMainWindow类型）
        const auto windowRect = widget->geometry();
        // 检查点是否在窗口区域内
        if (widget->isVisible() && widget->windowState() != Qt::WindowMinimized && windowRect.contains(QCursor::pos())) {
            return true;
        }
    }
    return false;
}

void ToolUtils::ShowMoveAnimation(QWidget* widget, const QPoint& startPos, const QPoint& endPos, int duration)
{
    auto animation = new QPropertyAnimation(widget, "pos");
    animation->setDuration(duration);
    // 设置起始坐标
    animation->setStartValue(startPos);
    // 设置结束坐标
    animation->setEndValue(endPos);
    animation->start();
    // // 等待动画结束
    connect(animation, &QPropertyAnimation::finished, [=](){
        animation->deleteLater();
    });
}
